转载一篇国外开发者写的技术文章,关于 JavaScript 的引擎、运行时、调用栈。
Overview
Almost everyone has already heard of the V8 Engine as a concept, and most people know that JavaScript is single-threaded or that it is using a callback queue.
In this post, we’ll go through all these concepts in detail and explain how JavaScript actually runs. By knowing these details, you’ll be able to write better, non-blocking apps that are properly leveraging the provided APIs.
If you’re relatively new to JavaScript, this blog post will help you understand why JavaScript is so “weird” compared to other languages.
And if you’re an experienced JavaScript developer, hopefully, it will give you some fresh insights on how the JavaScript Runtime you’re using every day actually works.
The JavaScript Engine
A popular example of a JavaScript Engine is Google’s V8 engine. The V8 engine is used inside Chrome and Node.js for example. Here is a very simplified view of what it looks like:

The Engine consists of two main components:
- Memory Heap — this is where the memory allocation happens
- Call Stack — this is where your stack frames are as your code executes
The runtime
There are APIs in the browser that have been used by almost any JavaScript developer out there (e.g. setTimeout
). Those APIs, however, are not provided by the Engine.
So, where are they coming from?
It turns out that the reality is a bit more complicated.

So, we have the Engine but there is actually a lot more. We have those things called Web APIs which are provided by browsers, like the DOM, AJAX, setTimeout and much more.
And then, we have the so popular event loop and the callback queue.
A runtime environment is the execution environment provided to an application by the operating system. In a runtime environment, the application can send instructions or commands to the processor and access other system resources such as RAM, DISK etc. JS engine, Event queues, Event loop and Web/Dom APIs forms the Runtime Environment.
The Call Stack
JavaScript is a single-threaded programming language, which means it has a single Call Stack. Therefore it can do one thing at a time.
The Call Stack is a data structure which records basically where in the program we are. If we step into a function, we put it on the top of the stack. If we return from a function, we pop off the top of the stack. That’s all the stack can do.
Let’s see an example. Take a look at the following code:
1 | function multiply(x, y) { |
Running code on a single thread can be quite easy since you don’t have to deal with complicated scenarios that are arising in multi-threaded environments — for example, deadlocks.
But running on a single thread is quite limiting as well. Since JavaScript has a single Call Stack, what happens when things are slow?
Concurrency & the Event Loop
What happens when you have function calls in the Call Stack that take a huge amount of time in order to be processed? For example, imagine that you want to do some complex image transformation with JavaScript in the browser.
You may ask — why is this even a problem? The problem is that while the Call Stack has functions to execute, the browser can’t actually do anything else — it’s getting blocked. This means that the browser can’t render, it can’t run any other code, it’s just stuck. And this creates problems if you want nice fluid UIs in your app.
And that’s not the only problem. Once your browser starts processing so many tasks in the Call Stack, it may stop being responsive for quite a long time. And most browsers take action by raising an error, asking you whether you want to terminate the web page.
ow, that’s not the best user experience out there, is it?
So, how can we execute heavy code without blocking the UI and making the browser unresponsive? Well, the solution is asynchronous callbacks.
This will be explained in greater detail in Part 2 of the “How JavaScript actually works” tutorial: “Inside the V8 engine + 5 tips on how to write optimized code”.